/**
 * 
 */
package cn.edu.cqu.bysj.wm.ago.gn;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;

import cn.edu.cqu.bysj.wm.def.PropertyDef;
import cn.edu.cqu.bysj.wm.def.RelTypeDef;
import cn.edu.cqu.bysj.wm.neo4j.Neo4jTraversal;
import cn.edu.cqu.bysj.wm.util.NodeComparator;

/**
 * @author Meng
 * 
 */
public class HierarchyTree {

	private GraphDatabaseService tdb;
	private Node root = null;

	// map of communtiy id and nodes after the construction of htree
	public Map<Long, List<Node>> communitys;

	/**
	 * initialize htree
	 * 
	 * @param list
	 *            all nodes in the net
	 * @param tdb
	 *            tree database service
	 */
	public void initTree(List<Node> list, GraphDatabaseService tdb) {

		communitys = new HashMap<Long, List<Node>>();
		this.tdb = tdb;
		Transaction tx = tdb.beginTx();
		try {
			root = tdb.createNode();

			root.setProperty(PropertyDef.COMMUNITY_SIZE, list.size());
			root.setProperty(PropertyDef.COMMUNITY_ID, 1);
			root.setProperty(PropertyDef.LEVEL, 0);

			tx.success();
		} finally {
			tx.finish();
			communitys.put(root.getId(), list);
		}
	}

	/**
	 * add child to htree
	 * 
	 * @param pid
	 *            parent community id
	 * @param list
	 *            node list of child commutniy
	 * 
	 * @return child communtiy id
	 */
	public long addChild(long pid, List<Node> list) {
		Transaction tx = tdb.beginTx();
		long cid = -1;
		try {
			Node child = tdb.createNode();
			Node parent = tdb.getNodeById(pid);

			cid = child.getId();

			parent.createRelationshipTo(child, RelTypeDef.PARENT_OF);

			child.setProperty(PropertyDef.COMMUNITY_ID, cid);
			child.setProperty(PropertyDef.COMMUNITY_SIZE, list.size());
			child.setProperty(PropertyDef.LEVEL,
					(Integer) parent.getProperty(PropertyDef.LEVEL) + 1);

			tx.success();
		} finally {
			tx.finish();
			communitys.put(cid, list);
		}
		return cid;
	}

	/**
	 * get all children communitys
	 * 
	 * @param pid
	 *            parent communtiy id
	 * @return children communitys id list
	 */
	public List<Node> getChildren(long pid) {
		Node parent = tdb.getNodeById(pid);

		List<Node> list = new ArrayList<Node>();

		for (Relationship rel : parent.getRelationships(Direction.OUTGOING,
				RelTypeDef.PARENT_OF)) {
			Node child = rel.getEndNode();
			list.add(child);
		}
		Collections.sort(list, new NodeComparator());
		return list;
	}

	/**
	 * get the parent communtiy id of a communtiy
	 * 
	 * @param cid
	 *            communtiy id
	 * @return parent communtiy id
	 */
	public long getParent(long cid) {
		if (cid == 1)
			return -1;
		Node child = tdb.getNodeById(cid);
		Relationship rel = child
				.getRelationships(Direction.INCOMING, RelTypeDef.PARENT_OF)
				.iterator().next();
		Node parent = rel.getStartNode();
		return parent.getId();
	}

	/**
	 * get the depth of htree
	 * 
	 * @return depth
	 */
	public int getDepth() {
		Node root = tdb.getNodeById(1);
		int depth = Neo4jTraversal.getTreeDepth(root);
		return depth;
	}

	/**
	 * get the community level
	 * 
	 * @param id
	 *            community id
	 * @return level
	 */
	public int getLevel(long id) {
		Node node = tdb.getNodeById(id);
		int level = (Integer) node.getProperty(PropertyDef.LEVEL);
		return level;
	}

	/**
	 * get all communitys in the depth
	 * 
	 * @param depth
	 *            depth want to get in
	 * @return community list
	 */
	public List<Node> getCommunitysByDepth(int depth) {
		return Neo4jTraversal.getLeafNodesByDepth(root, depth);
	}
}
